/** ***********************************************************************************
* @file         flash_dev.c
* @brief        FLASH底层驱动接口
* @details      主要用来进行对FLASH底层驱动接口进行读、写、擦除等操作
* @author       杨小珛
* @date         2020/03/03
* @copyright    北京高知科技有限公司    
* @version      V1.0
* @attention    无
**************************************************************************************/
#include "flash_dev.h"
#include "flash_config.h"


GaozMutexHandle_t flash_spi_mutex; /*FLASH 互斥体*/
uint32_t flash_spi_fd;  /*flash spi设备的节点*/

/** ****************************************************************************
* @remarks       void flash_spi_dev_init(void)
* @brief         初始化SPI驱动
* @param[in]     无
* @param[out]    无
* @return        pdPASS 初始成功 pdFAIL 初始化
* @attention     无
*******************************************************************************/
uint32_t flash_spi_dev_init(void)
{    
    uint32_t ret = pdFAIL , i = 0;
    drvSpiConfig_t cfg = { 0,};
    /*设置IO口的模式*/
    gaoz_gpio_mode_set( 37 ,1 );
    gaoz_gpio_mode_set( 38 ,1 );
    gaoz_gpio_mode_set( 39 ,1 );
    gaoz_gpio_mode_set( 40 ,1 );
    cfg.name = DRV_NAME_SPI1;
    cfg.inputEn = true;    
    cfg.baud = 500000;    
    cfg.cpol = SPI_CPOL_LOW;    
    cfg.cpha = SPI_CPHA_1Edge;    
    cfg.input_sel = SPI_DI_1;    
    cfg.transmode = SPI_DIRECT_IRQ;   
    cfg.cs_polarity0 = SPI_CS_ACTIVE_LOW;    
    cfg.framesize = 8;    
    if( gaoz_spi_open(cfg, &flash_spi_fd) == 0 ) /*初始SPI驱动成功*/
    {
        for( i = 0 ; i < 5 ; i ++ )
        {
            if( flash_spi_read_id() == FLASH_JEDEC_ID )
            {
                ret = pdPASS;
                break;
            }
        }
    }
    
    if( ret == pdPASS )
    {
        flash_spi_mutex = gaoz_mutex_create(); /*创建USB 数据接收互斥体*/
        if( flash_spi_mutex == 0 )
        {
            while( 1 ); /*创建失败*/
        }
    }
    
    return ret;
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_dev_open(void)
* @brief         系统SPI 接口打开
* @param[in]     无
* @param[out]    无
* @return        pdPASS 打开成功       pdFAIL 打开失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_dev_open(void)
{    
    uint32_t ret = pdFAIL ;
    drvSpiConfig_t cfg = { 0,};
    /*设置IO口的模式*/
    gaoz_gpio_mode_set( 37 ,1 );
    gaoz_gpio_mode_set( 38 ,1 );
    gaoz_gpio_mode_set( 39 ,1 );
    gaoz_gpio_mode_set( 40 ,1 );
    cfg.name = DRV_NAME_SPI1;
    cfg.inputEn = true;    
    cfg.baud = 500000;    
    cfg.cpol = SPI_CPOL_LOW;    
    cfg.cpha = SPI_CPHA_1Edge;    
    cfg.input_sel = SPI_DI_1;    
    cfg.transmode = SPI_DIRECT_IRQ;   
    cfg.cs_polarity0 = SPI_CS_ACTIVE_LOW;    
    cfg.framesize = 8;    
    if( gaoz_spi_open(cfg, &flash_spi_fd) == 0 )
    {
        ret = pdPASS;
    }
    return ret;
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_dev_close(void)
* @brief         系统SPI 接口关闭
* @param[in]     无
* @param[out]    无
* @return        pdPASS 关闭成功 pdFAIL 关闭失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_dev_close(void)
{    
    uint32_t ret = pdFAIL;
    if( gaoz_spi_close( &flash_spi_fd ) == 0 )
    {
        ret = pdPASS;
    }
    return ret;
}

/** ****************************************************************************
* @remarks       static void flash_spi_write_enable()
* @brief         FLASH 写使能
* @param[in]     无
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
void flash_spi_write_enable( void )
{    
    uint8_t OSI_ALIGNED(16) cmd = W25x_WRITE_ENABLE;    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send(flash_spi_fd, spiIoc, &cmd, 1);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
}

/** ****************************************************************************
* @remarks       void flash_spi_power_down( void )
* @brief         FLASH 低功耗模式
* @param[in]     无
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
void flash_spi_power_down( void )
{    
    uint8_t OSI_ALIGNED(16) cmd = W25X_POWER_DONE;    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send(flash_spi_fd, spiIoc, &cmd, 1);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
}

/** ****************************************************************************
* @remarks       void flash_spi_power_up( void )
* @brief         FLASH 待机模式
* @param[in]     无
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
void flash_spi_power_up( void )
{    
    uint8_t OSI_ALIGNED(16) cmd = W25X_POWER_UP;    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send(flash_spi_fd, spiIoc, &cmd, 1);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
}

/** ****************************************************************************
* @remarks       void flash_spi_write_disable( void )
* @brief         FLASH 写禁止
* @param[in]     无
* @param[out]    无
* @return        FLASH 的ID
* @attention     无
*******************************************************************************/
void flash_spi_write_disable( void )
{    
    uint8_t OSI_ALIGNED(16) cmd = W25x_WRITE_DISABLE;    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    if( flash_spi_fd == 0 )
    {
        return;
    }
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send(flash_spi_fd, spiIoc, &cmd, 1);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
}

/** ****************************************************************************
* @remarks       void flash_spi_read_id(void)
* @brief         读取外部FLASH 的ID地址
* @param[in]     无
* @param[out]    无
* @return        FLASH 的ID
* @attention     无
*******************************************************************************/
uint32_t flash_spi_read_id( void )
{    
    uint8_t OSI_ALIGNED(16) command = W25X_JEDEC_DEVICE_ID;
    uint8_t OSI_ALIGNED(16) flash_Id[3] = {0};    
    uint8_t OSI_ALIGNED(16) dummy_data[3] = {0};
    uint32_t id = 0;  
    SPI_IOC spiIoc =    
    {       
        8,
        500000,       
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    if( flash_spi_fd == 0 )
    {
        return 0;
    }
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send( flash_spi_fd, spiIoc, &command, 1 );
    gaoz_spi_recv( flash_spi_fd, spiIoc, dummy_data, flash_Id, 3 );
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
    id = ( flash_Id[0] << 16 ) | ( flash_Id[1] << 8 ) | flash_Id[2];
    return id;
}

/** ****************************************************************************
* @remarks       bool flash_spi_wait_write_fininsh(void)
* @brief         等待写操作完成
* @param[in]     无
* @param[out]    无
* @return        true 写成功 ，false 写失败
* @attention     无
*******************************************************************************/
bool flash_spi_wait_write_fininsh( void )
{ 
    uint8_t OSI_ALIGNED(16) cmd = W25X_READ_EARTUS_REG;    
    uint8_t OSI_ALIGNED(16) status = 0;    
    uint8_t OSI_ALIGNED(16) dummydata = 0; 
    uint32_t startTime = 0;
    SPI_IOC spiIoc =    
    { 
        8,
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };  
    if( flash_spi_fd == 0 )
    {
        return false; 
    }
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send( flash_spi_fd, spiIoc, &cmd, 1); 
    //startTime = osiUpTimeUS();
    do    
    {
        gaoz_task_sleep( 1 );
        gaoz_spi_recv( flash_spi_fd, spiIoc, &dummydata, &status, 1 );        
		startTime ++;
        //if ( osiUpTimeUS() - startTime > 2000000 ) 
        //{            
        //    return false;        
        //}
    } while((( status & 0x01 ) == 1 ) && ( startTime < 1000 ));
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
    
    return true;
}

/** ****************************************************************************
* @remarks       void flash_spi_sector_erase( uint32_t sectoraddr )
* @brief         擦除4K FLASH 空间
* @param[in]     sectoraddr 参数指定空间的地址
* @param[out]    无
* @return        pdPASS 擦除成功 ， pdFAIL 擦除失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_sector_erase( uint32_t sectoraddr )
{    
    uint8_t OSI_ALIGNED(16) cmd[5] = { 0 }; 
    uint8_t mutex = 0;
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };
    
    if( flash_spi_fd == 0 )
    {
        return pdFAIL; 
    }
    if( flash_spi_mutex != 0 )
    {
        mutex = 1;
        gaoz_mutex_lock( flash_spi_mutex ); 
    }
    flash_spi_write_enable();
    if( flash_spi_wait_write_fininsh() != true )    
    {    
        if( mutex == 1 )
        {
            gaoz_mutex_unlock( flash_spi_mutex ); 
        }
        return pdFAIL;    
    }    
    cmd[0] = W25X_SECTOR_ERASE;
    cmd[1] = (sectoraddr & 0xff0000) >> 16;
    cmd[2] = (sectoraddr & 0xff00) >> 8;
    cmd[3] = sectoraddr & 0xff;
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send( flash_spi_fd, spiIoc, cmd, 4);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
    if( flash_spi_wait_write_fininsh()    != true )
    {
        if( mutex == 1 )
        {
            gaoz_mutex_unlock( flash_spi_mutex ); 
        }
        return pdFAIL;   
    }
    flash_spi_write_disable();
    if( mutex == 1 )
    {
        gaoz_mutex_unlock( flash_spi_mutex );
    }
    return pdPASS;
}

/** ****************************************************************************
* @remarks       uint32_t  flash_spi_block_erase( uint32_t sectoraddr, uint32_t num_erase )
* @brief         擦出数据块
* @param[in]     start_sddr 开始地址
* @param[in]     num_erase 要擦出的空间大小
* @param[out]    无
* @return        pdPASS 擦除成功 ， pdFAIL 擦除失败
* @attention     无
*******************************************************************************/
uint32_t  spi_flash_block_erase( uint32_t start_sddr, uint32_t num_erase )
{
    uint32_t  startaddr = start_sddr, endaddr = 0, i = 0;
    
    if(( start_sddr + num_erase ) > ( SPI_FLASH_BASE_ADDR + SPI_FLASH_SIZE ) )
    {
        return pdFAIL;
    }    
    if( num_erase == 0 )
    {
        return pdFAIL;
    }    
    endaddr = start_sddr + num_erase;
    startaddr = start_sddr - ( start_sddr % SPI_FLASH_MIN_SECTOR );
    endaddr = endaddr + ( SPI_FLASH_MIN_SECTOR - endaddr % SPI_FLASH_MIN_SECTOR );
    
    for( i = startaddr; i < endaddr; i += SPI_FLASH_MIN_SECTOR )
    {
        flash_spi_sector_erase( i );
    }

    return pdPASS;
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_chip_erase( uint32_t sectoraddr )
* @brief         擦除芯片空间
* @param[in]     sectoraddr 参数指定空间的地址
* @param[out]    无
* @return        pdPASS 擦除成功 ， pdFAIL 擦除失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_chip_erase( void )
{    
    uint8_t OSI_ALIGNED(16) cmd  = W25X_CHIP_ERASE; 
    uint8_t mutex = 0;

    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };
    if( flash_spi_fd == 0 )
    {
        return pdFAIL; 
    }
    if( flash_spi_mutex != 0 )
    {
        mutex = 1;
        gaoz_mutex_lock( flash_spi_mutex ); 
    }
    flash_spi_write_enable();
    if( !flash_spi_wait_write_fininsh() )    
    {  
        if( mutex == 1)
        {
            gaoz_mutex_unlock( flash_spi_mutex ); 
        }
        return pdFAIL;    
    }    
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL );
    gaoz_spi_send( flash_spi_fd, spiIoc, &cmd, 1);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FROCE_1_CTRL );
    if( !flash_spi_wait_write_fininsh() )
    {     
        if( mutex == 1)
        {
            gaoz_mutex_unlock( flash_spi_mutex ); 
        }
        return pdFAIL;   
    }
    flash_spi_write_disable();
    if( mutex == 1)
    {
        gaoz_mutex_unlock( flash_spi_mutex ); 
    }
    
    return pdPASS;
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_page_write( uint32_t addr, uint8_t *pbuff, uint16_t size )
* @brief         写FLASH 页空间数据 
* @param[in]     addr 写空间地址
* @param[in]     *pbuff 写数据地址
* @param[in]     size 写数据长度
* @param[out]    无
* @return        pdPASS 写成功 ， pdFAIL 写失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_page_write( uint32_t addr, uint8_t *pbuff, uint16_t size )
{    
    uint8_t OSI_ALIGNED(16) cmd[5] = { 0 }; 
    uint8_t mutex = 0;
    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };   
    
    if( flash_spi_fd == 0 )
    {
        return pdFAIL; 
    }
    if( flash_spi_mutex != 0 )
    {
        mutex = 1;
        gaoz_mutex_lock( flash_spi_mutex ); 
    }
    flash_spi_write_enable();
    cmd[0] = W25X_PAGE_PROGRAM;    
    cmd[1] = ( addr & 0xff0000) >> 16;    
    cmd[2] = ( addr & 0xff00) >> 8;    
    cmd[3] = addr & 0xff;    
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL);    
    gaoz_spi_send( flash_spi_fd, spiIoc, cmd, 4);    
    if( size > SPI_FLASH_PAGE_SIZE )
    {        
        size = SPI_FLASH_PAGE_SIZE;   
    }    
    gaoz_spi_send( flash_spi_fd, spiIoc, pbuff, size);
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, SPI_CTRL);    
    if( flash_spi_wait_write_fininsh() != true )    
    {
        if( mutex == 1)
        {
            gaoz_mutex_unlock( flash_spi_mutex ); 
        }
        return pdFAIL;
    }
    flash_spi_write_disable();
    
    if( mutex == 1)
    {
        gaoz_mutex_unlock( flash_spi_mutex ); 
    }
    return pdPASS; 
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_write_data( uint32_t addr ,uint8_t *pbuff, uint16_t size )
* @brief         写FLASH 空间数据 
* @param[in]     addr 写空间地址
* @param[in]     *pbuff 写数据地址
* @param[in]     size 写数据长度
* @param[out]    无
* @return        pdPASS 写成功 ， pdFAIL 写失败
* @attention     无
*******************************************************************************/
uint32_t flash_spi_write_data( uint32_t addr ,uint8_t *pbuff, uint16_t size )
{    
    uint8_t waddr = addr % SPI_FLASH_PAGE_SIZE;    
    uint8_t count = SPI_FLASH_PAGE_SIZE - waddr;   
    uint16_t numofpage = size / SPI_FLASH_PAGE_SIZE;    
    uint16_t numofsigle = size % SPI_FLASH_PAGE_SIZE;    
    uint16_t temp = 0;
    
    if (waddr == 0)    
    {        
        if (numofpage == 0)        
        {            
            if( flash_spi_page_write(addr, pbuff, size) == pdFAIL )
            {
                return pdFAIL;
            }
        }
        else        
        {            
            while (numofpage--)            
            {                
                if( flash_spi_page_write(addr, pbuff, SPI_FLASH_PAGE_SIZE) == pdFAIL )
                {
                    return pdFAIL;
                }
                addr += SPI_FLASH_PAGE_SIZE;                
                pbuff += SPI_FLASH_PAGE_SIZE;            
            }            
            if( flash_spi_page_write(addr, pbuff, numofsigle) == pdFAIL )
            {
                return pdFAIL;
            }
        }    
    }    
    else    
    {        
        if (numofpage == 0)        
        {            
            if (numofsigle > count)
            {                
                temp = numofsigle - count;                
                if( flash_spi_page_write(addr, pbuff, count) == pdFAIL )
                {
                    return pdFAIL;
                }
                addr+= count;                
                pbuff += count;                
                if( flash_spi_page_write(addr, pbuff, temp) == pdFAIL )
                {
                    return pdFAIL;
                }
            }            
            else            
            {                
                if( flash_spi_page_write(addr, pbuff, size) == pdFAIL )
                {
                    return pdFAIL;
                }
            }        
        }        
        else        
        {            
            size -= count;           
            numofpage = size / SPI_FLASH_PAGE_SIZE;            
            numofsigle = size % SPI_FLASH_PAGE_SIZE;            
            if( flash_spi_page_write(addr, pbuff, count) == pdFAIL )
            {
                return pdFAIL;
            }
            addr += count;            
            pbuff += count;           
            while (numofpage--)            
            {                
                if( flash_spi_page_write(addr, pbuff, SPI_FLASH_PAGE_SIZE) == pdFAIL )
                {
                    return pdFAIL;
                }
                addr += SPI_FLASH_PAGE_SIZE;                
                pbuff += SPI_FLASH_PAGE_SIZE;            
            }            
            if (numofsigle != 0)            
            {                
                if( flash_spi_page_write(addr, pbuff, numofsigle) == pdFAIL )
                {
                    return pdFAIL;
                }
            }        
        }    
    }
    return pdPASS;
}

/** ****************************************************************************
* @remarks       uint32_t flash_spi_read_data( uint32_t addr, uint8_t *pbuff, uint16_t size )
* @brief         读取FLASH 数据
* @param[in]     addr flash地址
* @param[in]     *pbuff 读取数据地址
* @param[in]     size 读取数据的长度
* @param[out]    无
* @return        无
* @attention     无
*******************************************************************************/
uint32_t flash_spi_read_data( uint32_t addr, uint8_t *pbuff, uint16_t size )
{    
    uint8_t OSI_ALIGNED(16) cmd[5] = { 0 };
    uint8_t OSI_ALIGNED(16) dummpdata[size]; 
    uint8_t mutex = 0;
    
    SPI_IOC spiIoc =    
    {       
        8,      
        500000,        
        SPI_I2C_CS0,        
        SPI_CS_ACTIVE_LOW,  
    };    
    
    if( flash_spi_fd == 0 )
    {
        return pdFAIL;
    }
    if( flash_spi_mutex != 0 )
    {
        mutex = 1;
        gaoz_mutex_lock( flash_spi_mutex ); 
    }
    cmd[0] = W25X_READ_DATA;    
    cmd[1] = ( addr & 0xff0000 ) >> 16;    
    cmd[2] = ( addr & 0xff00 ) >> 8;    
    cmd[3] = addr & 0xff;
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, FORCE_0_CTRL);
    gaoz_spi_send( flash_spi_fd, spiIoc, cmd, 4);    
    gaoz_spi_recv( flash_spi_fd, spiIoc, dummpdata, pbuff, size );
    gaoz_spi_pinctrl( flash_spi_fd, SPI_CS0_CTRL, SPI_CTRL);
    if( mutex == 1)
    {
        gaoz_mutex_unlock( flash_spi_mutex ); 
    }
    return pdPASS; 
}

